package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.io.Serializable;

/**
 * "And the LORD appeared unto him in the plains of Mamre: and he sat in the 
 *  tent door in the heat of the day; And he lift up his eyes and looked, and, lo, 
 *  three men stood by him: and when he saw them , he ran to meet them from the tent door, 
 *  and bowed himself toward the ground, And said, My Lord, if now I have found favour 
 *  in thy sight, pass not away, I pray thee, from thy servant:" (Gen 18:1-3)
 * 
 * ==========
 * 
 * Period p: p.start is closed end and p.end is open end. So if p1.end == p2.start, they do not overlap.
 * @author jukka.rahkonen@iki.fi
 */
public class Period extends Jugile implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private Time start;
	private Time end;

	public Period(Time start, Time end) {
		if (start == null && end == null) 
			fail("Both start and end null. From eternity to eternity is not a secular Period.");
		if (start != null && end != null)
			if (start.gt(end)) fail("Invalid period: start after end");
		this.start = start;
		this.end = end;
	}
	
	public Period(Time time) {
		this.start = time;
		this.end = time;
	}
	
	public Period(String start, String end) {
		if (empty(start)) this.start = null;
		else this.start = new Time(start);
		if (empty(end)) this.end = null;
		else this.end = new Time(end);
	}
	
	public Time getStart() { return start; }
	public Time getEnd() { return end; }
	
	public Duration getDuration() {
		if (start == null || end == null) return new Duration(0); // infinite
		long s = start.getMs();
		long e = end.getMs();
		return new Duration(e-s);
	}

	
	/**
	 * Periods do not overlap if just p1.end == p2.start.
	 */
	public boolean overlaps(Period p) {
		if (p == null) return false;
		return !notOverlapping(p);
	}
	
	public boolean notOverlapping(Period p) {
		// case1: p1:  ----------o
		//        p2:              o----------
		
		// case2: p1:              o----------
		//        p2:  ---------o
		Time s1 = getStart();
		Time e1 = getEnd();
		Time s2 = p.getStart();
		Time e2 = p.getEnd();
		if (s1 == null && s2 == null) return false;
		if (e1 == null && e2 == null) return false;
		if (e2 != null && s1 != null && e2.lte(s1)) return true;
		if (e1 != null && s2 != null && e1.lte(s2)) return true;
		return false;
	}
	
	public Period getOverlap(Period p) {
		if (p == null) return null;
		if (notOverlapping(p)) return null;
		Time s1 = getStart();
		Time e1 = getEnd();
		Time s2 = p.getStart();
		Time e2 = p.getEnd();
		Time s = null;
		Time e = null;
		
		if (s1 == null) s = s2;
		else if (s2 == null) s = s1;
		else if (s1.gt(s2)) s = s1;
		else s = s2;

		if (e1 == null) e = e2;
		else if (e2 == null) e = e1;
		else if (e1.lt(e2)) e = e1;
		else e = e2;

		return new Period(s,e);
	}
	
	public Duration getOverlapDuration(Period p) {
		Period p2 = getOverlap(p);
		if (p2 == null) return new Duration(0);
		return p2.getDuration();
	}
	
	/**
	 * Period contains time t if p.start <= t < p.end.
	 */
	public boolean contains(Time t) {
		if (t == null) return false;
		if (start == null && end != null) 
			if (end.gt(t)) return true;
			else return false;
		if (start != null && end == null)
			if (start.lte(t)) return true;
			else return false;
		if (t.gte(start) && t.lt(end)) return true;
		return false;
	}

	
	public static boolean isValid(Time start, Time end, Time ts) {
		Period p = new Period(start,end);
		return p.contains(ts);
	}
	
	public String toString() {
		Buffer buf = new Buffer();
		if (start == null) buf.add("null");
		else buf.add(start.getUiTs());
		buf.add(" - ");
		if (end == null) buf.add("null");
		else buf.add(end.getUiTs());		
		return buf.toString();
	}
}
